package com.anasoft.os.s4j;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;

import com.anasoft.os.s4j.annotation.ErrorReporting;
import com.anasoft.os.s4j.annotation.SeleniumSession;
import com.thoughtworks.selenium.Selenium;

/**
 * JUnit {@link RunListener} that implements the error reporting
 * behavior of Selenium tests.
 * <p>
 * This run listener is configured via the {@link ErrorReporting}
 * annotation nested inside {@link SeleniumSession}.
 * 
 * @see ErrorReporting
 * @see SeleniumSession
 * @see SeleniumRunnerLogic
 * 
 * @author vojtech.szocs
 */
public class ErrorReportingListener extends RunListener {

    private final ErrorReporting errorConfig;
    private final Selenium session;
    
    public ErrorReportingListener(ErrorReporting errorConfig, Selenium session) {
        this.errorConfig = errorConfig;
        this.session = session;
    }
    
    /**
     * @see org.junit.runner.notification.RunListener#testFailure(org.junit.runner.notification.Failure)
     */
    @Override
    public void testFailure(Failure failure) throws Exception {
        try {
            if (errorConfig.enabled()) {
                if (errorConfig.captureScreenshots()) {
                    session.captureScreenshot(getScreenshotFileName(
                            failure, errorConfig.serverOutputDirectory()));
                }
                
                if (errorConfig.retrieveLastRemoteControlLogs()) {
                    writeFile(
                            getRemoteControlLogsFileName(failure, errorConfig.runnerOutputDirectory()),
                            session.retrieveLastRemoteControlLogs());
                }
            }
        }
        
        catch (Exception ex) {
            // can't let Exception fall through, as this would cause this RunListener
            // to be excluded from associated RunNotifier for subsequent tests
            if (!errorConfig.swallowExceptions())
                throw new Error("Error reporting listener execution failure", ex);
        }
    }
    
    /**
     * Override this method in case you want to use your own screenshot
     * file naming strategy.
     * 
     * @param failure Information about a failed test.
     * @param outputDirectory Output directory set via {@link ErrorReporting}.
     * @return Screenshot file name for the failed test.
     */
    protected String getScreenshotFileName(Failure failure, String outputDirectory) {
        return outputDirectory + failure.getDescription().getDisplayName() + ".png";
    }
    
    /**
     * Override this method in case you want to use your own server log
     * file naming strategy.
     * 
     * @param failure Information about a failed test.
     * @param outputDirectory Output directory set via {@link ErrorReporting}.
     * @return Server log file name for the failed test.
     */
    protected String getRemoteControlLogsFileName(Failure failure, String outputDirectory) {
        return outputDirectory + failure.getDescription().getDisplayName() + ".log";
    }
    
    private void writeFile(String fileName, String contents) {
        BufferedWriter out = null;
        
        try {
            out = new BufferedWriter(new FileWriter(fileName));
            out.write(contents);
            out.close();
        } catch (IOException ioex) {
            // ignore I/O errors silently
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException ioex) {
                    // can't close the output stream, nothing to do here
                }
            }
        }
    }
    
}
